package cn.zxh.improvement.gencode.util;

import cn.zxh.improvement.gencode.processor.pojo.Doc;
import com.squareup.javapoet.*;

import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;

/**
 * 代码生成工具类
 *
 * @author zxh
 * @since 2023/5/15 15:08:26
 */
public class GenCodeUtil {

    /**
     * 生成java 源文件
     * TypeSpec 代表一个类
     *
     * @param packageName 包名
     * @param pathStr     路径
     * @param typeSpec    构建
     */
    public static void genJavaSourceFile(String packageName, String pathStr, TypeSpec typeSpec) {
        JavaFile javaFile = JavaFile
                .builder(packageName, typeSpec)
                .addFileComment("---Auto Generated by myModule ---")
                .build();
        String packagePath = packageName.replace(".", File.separator) + File.separator + typeSpec.name + ".java";
        try {
            Path path = Paths.get(pathStr);
            File file = new File(path.toFile().getAbsolutePath());
            if (!file.exists()) {
                return;
            }
            String sourceFileName = path.toFile().getAbsolutePath() + File.separator + packagePath;
            File sourceFile = new File(sourceFileName);
            if (!sourceFile.exists()) {
                javaFile.writeTo(file);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 生成Java文件
     *
     * @param typeSpec
     * @param packageName
     * @param outputDir
     * @throws IOException
     */
    public static void generateJavaFile(TypeSpec typeSpec, String packageName, String outputDir) throws IOException {
        JavaFile javaFile = JavaFile.builder(packageName, typeSpec)
                .indent("    ")
                .build();
        javaFile.writeTo(new File(outputDir));
    }

    /**
     * 生成私有字段属性
     *
     * @param attribute 属性
     * @param desc      描述
     * @param typeName  类型
     * @return 字段
     */
    public static FieldSpec genPrivateField(String attribute, String desc, TypeName typeName) {
        return FieldSpec.builder(typeName, attribute)
                .addModifiers(Modifier.PRIVATE)
                .addJavadoc(desc)
                .build();
    }

    /**
     * 生成 set 方法
     *
     * @param attribute 属性
     * @param desc      描述
     * @param typeName  属性类型
     * @return set方法
     */
    public static MethodSpec genSetMethod(String attribute, String desc, TypeName typeName) {
        // 泛型 ParameterizedTypeName.get(ClassName.get(JsonObject.class), ClassName.get(String.class))
        return MethodSpec.methodBuilder("set" + getInitialCaps(attribute))
                .returns(TypeName.VOID)
                .addJavadoc(desc)
                .addModifiers(Modifier.PUBLIC)
                .addParameter(typeName, attribute)
                .addStatement("this.$L = $L", attribute, attribute)
                .build();
    }

    /**
     * 生成 get 方法
     *
     * @param attribute 属性
     * @param typeName  属性类型
     * @return get方法
     */
    public static MethodSpec genGetMethod(String attribute, String desc, TypeName typeName) {
        return MethodSpec.methodBuilder("get" + getInitialCaps(attribute))
                .addModifiers(Modifier.PUBLIC)
                .addJavadoc(desc)
                .returns(typeName)
                .addStatement("return $L", attribute)
                .build();
    }

    /**
     * 查找变量的属性
     * predicate用来过滤掉不要的属性
     *
     * @param typeElement 注解对在上面的对象
     * @param predicate   判断校验通过的字段，一般就是为了跟忽略字段配套使用
     * @return 属性集合
     */
    public static Set<VariableElement> findFields(TypeElement typeElement, Predicate<VariableElement> predicate) {
        List<? extends Element> fieldTypes = typeElement.getEnclosedElements();
        Set<VariableElement> variableElements = new LinkedHashSet<>();

        for (VariableElement e : ElementFilter.fieldsIn(fieldTypes)) {
            if (predicate.test(e)) {
                variableElements.add(e);
            }
        }
        return variableElements;
    }

    /**
     * 获取首字母大写
     *
     * @param attribute 属性
     * @return 首字母大写
     */
    protected static String getInitialCaps(String attribute) {
        return attribute.substring(0, 1).toUpperCase() + attribute.substring(1);
    }

    /**
     * 获取字段描述
     *
     * @param element 元素
     * @return 描述
     */
    public static String getFileDesc(VariableElement element) {
        Doc annotation = element.getAnnotation(Doc.class);
        if (Objects.isNull(annotation)) {
            return "";
        } else {
            return annotation.desc();
        }
    }
}
